{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 20000 images belonging to 10 classes.\n",
      "Found 4000 images belonging to 10 classes.\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "import numpy as np\n",
    "from keras.models import Sequential\n",
    "from keras.layers import Activation, Dropout, Flatten, Dense\n",
    "from keras.preprocessing.image import ImageDataGenerator\n",
    "from keras.layers import Conv2D, MaxPooling2D, ZeroPadding2D\n",
    "from keras import optimizers\n",
    "import keras\n",
    "\n",
    "input_shape = (32, 32, 3)\n",
    "img_width = 32\n",
    "img_height = 32\n",
    "num_classes = 10\n",
    "nb_train_samples = 10000\n",
    "nb_validation_samples = 2000\n",
    "batch_size = 16\n",
    "epochs = 1\n",
    "\n",
    "train_data_dir = './credit_card/train'\n",
    "validation_data_dir = './credit_card/test'\n",
    "\n",
    "# Creating our data generator for our test data\n",
    "validation_datagen = ImageDataGenerator(\n",
    "    # used to rescale the pixel values from [0, 255] to [0, 1] interval\n",
    "    rescale = 1./255)\n",
    "\n",
    "# Creating our data generator for our training data\n",
    "train_datagen = ImageDataGenerator(\n",
    "      rescale = 1./255,              # normalize pixel values to [0,1]\n",
    "      rotation_range = 10,           # randomly applies rotations\n",
    "      width_shift_range = 0.25,       # randomly applies width shifting\n",
    "      height_shift_range = 0.25,      # randomly applies height shifting\n",
    "      shear_range=0.5,\n",
    "      zoom_range=0.5,\n",
    "      horizontal_flip = False,        # randonly flips the image\n",
    "      fill_mode = 'nearest')         # uses the fill mode nearest to fill gaps created by the above\n",
    "\n",
    "# Specify criteria about our training data, such as the directory, image size, batch size and type \n",
    "# automagically retrieve images and their classes for train and validation sets\n",
    "train_generator = train_datagen.flow_from_directory(\n",
    "        train_data_dir,\n",
    "        target_size = (img_width, img_height),\n",
    "        batch_size = batch_size,\n",
    "        class_mode = 'categorical')\n",
    "\n",
    "validation_generator = validation_datagen.flow_from_directory(\n",
    "        validation_data_dir,\n",
    "        target_size = (img_width, img_height),\n",
    "        batch_size = batch_size,\n",
    "        class_mode = 'categorical',\n",
    "        shuffle = False)    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "conv2d_3 (Conv2D)            (None, 32, 32, 20)        1520      \n",
      "_________________________________________________________________\n",
      "activation_5 (Activation)    (None, 32, 32, 20)        0         \n",
      "_________________________________________________________________\n",
      "max_pooling2d_3 (MaxPooling2 (None, 16, 16, 20)        0         \n",
      "_________________________________________________________________\n",
      "conv2d_4 (Conv2D)            (None, 16, 16, 50)        25050     \n",
      "_________________________________________________________________\n",
      "activation_6 (Activation)    (None, 16, 16, 50)        0         \n",
      "_________________________________________________________________\n",
      "max_pooling2d_4 (MaxPooling2 (None, 8, 8, 50)          0         \n",
      "_________________________________________________________________\n",
      "flatten_2 (Flatten)          (None, 3200)              0         \n",
      "_________________________________________________________________\n",
      "dense_3 (Dense)              (None, 500)               1600500   \n",
      "_________________________________________________________________\n",
      "activation_7 (Activation)    (None, 500)               0         \n",
      "_________________________________________________________________\n",
      "dense_4 (Dense)              (None, 10)                5010      \n",
      "_________________________________________________________________\n",
      "activation_8 (Activation)    (None, 10)                0         \n",
      "=================================================================\n",
      "Total params: 1,632,080\n",
      "Trainable params: 1,632,080\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "# create model\n",
    "model = Sequential()\n",
    "\n",
    "# 2 sets of CRP (Convolution, RELU, Pooling)\n",
    "model.add(Conv2D(20, (5, 5),\n",
    "                 padding = \"same\", \n",
    "                 input_shape = input_shape))\n",
    "model.add(Activation(\"relu\"))\n",
    "model.add(MaxPooling2D(pool_size = (2, 2), strides = (2, 2)))\n",
    "\n",
    "model.add(Conv2D(50, (5, 5),\n",
    "                 padding = \"same\"))\n",
    "model.add(Activation(\"relu\"))\n",
    "model.add(MaxPooling2D(pool_size = (2, 2), strides = (2, 2)))\n",
    "\n",
    "# Fully connected layers (w/ RELU)\n",
    "model.add(Flatten())\n",
    "model.add(Dense(500))\n",
    "model.add(Activation(\"relu\"))\n",
    "\n",
    "# Softmax (for classification)\n",
    "model.add(Dense(num_classes))\n",
    "model.add(Activation(\"softmax\"))\n",
    "           \n",
    "model.compile(loss = 'categorical_crossentropy',\n",
    "              optimizer = keras.optimizers.Adadelta(),\n",
    "              metrics = ['accuracy'])\n",
    "    \n",
    "print(model.summary())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/5\n",
      "1250/1250 [==============================] - 105s 84ms/step - loss: 0.4301 - acc: 0.8571 - val_loss: 0.0022 - val_acc: 1.0000\n",
      "\n",
      "Epoch 00001: val_loss improved from inf to 0.00220, saving model to /home/deeplearningcv/DeepLearningCV/Trained Models/creditcard.h5\n",
      "Epoch 2/5\n",
      "1250/1250 [==============================] - 116s 93ms/step - loss: 0.1545 - acc: 0.9494 - val_loss: 0.0014 - val_acc: 0.9998\n",
      "\n",
      "Epoch 00002: val_loss improved from 0.00220 to 0.00135, saving model to /home/deeplearningcv/DeepLearningCV/Trained Models/creditcard.h5\n",
      "Epoch 3/5\n",
      "1250/1250 [==============================] - 170s 136ms/step - loss: 0.1200 - acc: 0.9596 - val_loss: 0.0015 - val_acc: 0.9995\n",
      "\n",
      "Epoch 00003: val_loss did not improve from 0.00135\n",
      "Epoch 4/5\n",
      "1250/1250 [==============================] - 158s 126ms/step - loss: 0.1046 - acc: 0.9672 - val_loss: 0.0093 - val_acc: 0.9990\n",
      "\n",
      "Epoch 00004: val_loss did not improve from 0.00135\n",
      "Epoch 5/5\n",
      "1250/1250 [==============================] - 113s 90ms/step - loss: 0.0999 - acc: 0.9703 - val_loss: 0.0020 - val_acc: 0.9992\n",
      "Restoring model weights from the end of the best epoch\n",
      "\n",
      "Epoch 00005: val_loss did not improve from 0.00135\n",
      "Epoch 00005: early stopping\n"
     ]
    }
   ],
   "source": [
    "from keras.optimizers import RMSprop\n",
    "from keras.callbacks import ModelCheckpoint, EarlyStopping\n",
    "                   \n",
    "checkpoint = ModelCheckpoint(\"/home/deeplearningcv/DeepLearningCV/Trained Models/creditcard.h5\",\n",
    "                             monitor=\"val_loss\",\n",
    "                             mode=\"min\",\n",
    "                             save_best_only = True,\n",
    "                             verbose=1)\n",
    "\n",
    "earlystop = EarlyStopping(monitor = 'val_loss', \n",
    "                          min_delta = 0, \n",
    "                          patience = 3,\n",
    "                          verbose = 1,\n",
    "                          restore_best_weights = True)\n",
    "\n",
    "# we put our call backs into a callback list\n",
    "callbacks = [earlystop, checkpoint]\n",
    "\n",
    "# Note we use a very small learning rate \n",
    "model.compile(loss = 'categorical_crossentropy',\n",
    "              optimizer = RMSprop(lr = 0.001),\n",
    "              metrics = ['accuracy'])\n",
    "\n",
    "nb_train_samples = 20000\n",
    "nb_validation_samples = 4000\n",
    "epochs = 5\n",
    "batch_size = 16\n",
    "\n",
    "history = model.fit_generator(\n",
    "    train_generator,\n",
    "    steps_per_epoch = nb_train_samples // batch_size,\n",
    "    epochs = epochs,\n",
    "    callbacks = callbacks,\n",
    "    validation_data = validation_generator,\n",
    "    validation_steps = nb_validation_samples // batch_size)\n",
    "\n",
    "model.save(\"/home/deeplearningcv/DeepLearningCV/Trained Models/creditcard.h5\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from keras.models import load_model\n",
    "\n",
    "classifier = load_model('/home/deeplearningcv/DeepLearningCV/Trained Models/creditcard.h5')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[5]\n",
      "[4]\n",
      "[7]\n",
      "[6]\n",
      "[7]\n",
      "[6]\n",
      "[7]\n",
      "[8]\n",
      "[9]\n",
      "[8]\n",
      "[7]\n",
      "[6]\n",
      "[5]\n",
      "[4]\n",
      "[3]\n",
      "[2]\n"
     ]
    }
   ],
   "source": [
    "import cv2\n",
    "from keras.preprocessing import image\n",
    "\n",
    "def pre_process(image, inv = False):\n",
    "    try:\n",
    "        gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
    "    except:\n",
    "        gray_image = image\n",
    "        pass\n",
    "\n",
    "    kernel = np.ones((3,3), np.uint8)\n",
    "    \n",
    "    if inv == False:\n",
    "        _, th2 = cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
    "    else:\n",
    "        _, th2 = cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)\n",
    "    #dilation = cv2.dilate(th2, kernel, iterations = 1)\n",
    "    resized = cv2.resize(th2, (32,32), interpolation = cv2.INTER_AREA)\n",
    "    return resized\n",
    "\n",
    "image = cv2.imread('credit_card.jpg')\n",
    "resized = cv2.resize(image, (640,403), interpolation = cv2.INTER_AREA)\n",
    "\n",
    "ROI = ([(66, 220), (92, 262)], [(92, 12), (295, 25)])\n",
    "region = ROI[0]\n",
    "\n",
    "top_left_y = region[0][1]\n",
    "bottom_right_y = region[1][1]\n",
    "top_left_x = region[0][0]\n",
    "bottom_right_x = region[1][0]\n",
    "\n",
    "for i in range(0,16):\n",
    "    if i > 0 and i%4  == 0:\n",
    "        jump = 30\n",
    "    else:\n",
    "        jump = 0\n",
    "    \n",
    "    if i > 0:\n",
    "        top_left_x = top_left_x + 26 + jump\n",
    "        bottom_right_x = bottom_right_x + 26 + jump\n",
    "\n",
    "    roi = resized[top_left_y:bottom_right_y, top_left_x:bottom_right_x]\n",
    "    roi_otsu = pre_process(roi)\n",
    "    cv2.imshow(\"roi\", roi)\n",
    "    cv2.imshow(\"roi_otsu\", roi_otsu)\n",
    "    roi_otsu = cv2.cvtColor(roi_otsu, cv2.COLOR_GRAY2RGB)\n",
    "    x = keras.preprocessing.image.img_to_array(roi_otsu)\n",
    "    x = x * 1./255\n",
    "    x = np.expand_dims(x, axis=0)\n",
    "    image = np.vstack([x])\n",
    "    label = classifier.predict_classes(image, batch_size = 10)\n",
    "    print(label)\n",
    "    cv2.waitKey(0)\n",
    "    \n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def pre_process(image, inv = False):\n",
    "    try:\n",
    "        gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
    "    except:\n",
    "        gray_image = image\n",
    "        pass\n",
    "\n",
    "    kernel = np.ones((3,3), np.uint8)\n",
    "    \n",
    "    if inv == False:\n",
    "        _, th2 = cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
    "    else:\n",
    "        _, th2 = cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)\n",
    "    #dilation = cv2.dilate(th2, kernel, iterations = 1)\n",
    "    resized = cv2.resize(th2, (32,32), interpolation = cv2.INTER_AREA)\n",
    "    return resized\n",
    "\n",
    "def x_cord_contour(contour):\n",
    "    # This function take a contour from findContours\n",
    "    # it then outputs the x centroid coordinates\n",
    "    M = cv2.moments(contour)\n",
    "    return (int(M['m10']/M['m00']))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[5]\n",
      "[3]\n",
      "[5]\n",
      "[5]\n",
      "[2]\n",
      "[2]\n",
      "[0]\n",
      "[3]\n",
      "[2]\n",
      "[3]\n",
      "[9]\n",
      "[0]\n",
      "[0]\n",
      "[7]\n",
      "[5]\n",
      "[7]\n"
     ]
    }
   ],
   "source": [
    "img = cv2.imread('card_numbers.jpg')\n",
    "orig_img = cv2.imread('credit_card_color.jpg')\n",
    "gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)\n",
    "cv2.imshow(\"image\", img)\n",
    "cv2.waitKey(0)\n",
    "\n",
    "# Blur image then find edges using Canny \n",
    "blurred = cv2.GaussianBlur(gray, (5, 5), 0)\n",
    "#cv2.imshow(\"blurred\", blurred)\n",
    "#cv2.waitKey(0)\n",
    "\n",
    "edged = cv2.Canny(blurred, 30, 150)\n",
    "#cv2.imshow(\"edged\", edged)\n",
    "#cv2.waitKey(0)\n",
    "\n",
    "# Find Contours\n",
    "_, contours, _ = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "#Sort out contours left to right by using their x cordinates\n",
    "contours = sorted(contours, key = x_cord_contour, reverse = False)\n",
    "\n",
    "# Create empty array to store entire number\n",
    "full_number = []\n",
    "\n",
    "# loop over the contours\n",
    "for c in contours:\n",
    "    # compute the bounding box for the rectangle\n",
    "    (x, y, w, h) = cv2.boundingRect(c)    \n",
    "\n",
    "    if w >= 5 and h >= 25:\n",
    "        roi = blurred[y:y + h, x:x + w]\n",
    "        #ret, roi = cv2.threshold(roi, 20, 255,cv2.THRESH_BINARY_INV)\n",
    "        cv2.imshow(\"ROI1\", roi)\n",
    "        roi_otsu = pre_process(roi, True)\n",
    "        cv2.imshow(\"ROI2\", roi_otsu)\n",
    "        roi_otsu = cv2.cvtColor(roi_otsu, cv2.COLOR_GRAY2RGB)\n",
    "        roi_otsu = keras.preprocessing.image.img_to_array(roi_otsu)\n",
    "        roi_otsu = roi_otsu * 1./255\n",
    "        roi_otsu = np.expand_dims(roi_otsu, axis=0)\n",
    "        image = np.vstack([roi_otsu])\n",
    "        label = str(classifier.predict_classes(image, batch_size = 10))\n",
    "        print(label)\n",
    "        cv2.rectangle(orig_img, (x, y), (x + w, y + h), (0, 0, 255), 2)\n",
    "        cv2.putText(orig_img, label, (x , y + 155), cv2.FONT_HERSHEY_COMPLEX, 1, (255, 0, 0), 1)\n",
    "        cv2.imshow(\"image\", orig_img)\n",
    "        cv2.waitKey(0) \n",
    "        \n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(549, 7, 25, 38)"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(x, y, w, h)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
